#if !defined(DATA_SCENE_IMPL_H_)
#define DATA_SCENE_IMPL_H_
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

#include "data_scene.h"

namespace autodrive {
namespace perception {
class DataSceneImpl {
 public:
  /// @brief invalid index
  static constexpr std::int32_t invalidIndex = -1;

  using SyncInfoData = std::unordered_map<std::string, std::uint64_t>;
  using SyncInfo = std::unordered_map<std::string, SyncInfoData>;
  using SyncInfoDict = std::unordered_map<std::string, SyncInfo>;

 public:
  DataSceneImpl()
      : is_init_{false},
        data_frame_keys_ptr_{nullptr},
        sync_info_files_ptr_{nullptr},
        calib_data_ptr_{nullptr} {};
  DataSceneImpl(DataSceneImpl &&) = delete;
  DataSceneImpl(const DataSceneImpl &) = delete;
  DataSceneImpl &operator=(DataSceneImpl &&) = delete;
  DataSceneImpl &operator=(const DataSceneImpl &) = delete;
  ~DataSceneImpl();

 public:
  /// @brief
  /// @param path
  void Init(std::filesystem::path const &path) noexcept(false);

  /// @brief
  /// @return
  inline bool IsInit() const noexcept { return is_init_; }

  /// @brief
  /// @return
  inline DataScene::DataFrameArrayPtr<std::string> DataframeKeys()
      const noexcept {
    return data_frame_keys_ptr_;
  }

  /// @brief
  /// @param cameraNames
  /// @param key
  /// @param cameraDataArray
  void LoadCameraData(std::vector<std::string> const &cameraNames,
                      std::string const &key,
                      DataScene::CameraDataArray &cameraDataArray) const
      noexcept(false);

  /// @brief
  /// @param lidarNames
  /// @param key
  /// @param dataArray
  void LoadLidarData(std::vector<std::string> const &lidarNames,
                     std::string const &key,
                     DataScene::LidarDataArray &lidarDataArray) const
      noexcept(false);

  /// @brief
  /// @param radarNames
  /// @param key
  /// @param radarDataArray
  void LoadRadarData(std::vector<std::string> const &radarNames,
                     std::string const &key,
                     DataScene::radarDataArray &radarDataArray) const
      noexcept(false);

  /// @brief
  /// @param key
  /// @param egoData
  void LoadEgoMotionData(std::string const &key,
                         DataScene::MatrixData &egoData) const noexcept(false);

  /// @brief
  /// @return
  inline std::filesystem::path const &Path() const noexcept { return path_; }

  /// @brief
  /// @return
  inline DataScene::CalibDataArrayPtr<DataScene::MatrixData> CalibData()
      const noexcept {
    return calib_data_ptr_;
  }

  /// @brief
  /// @return
  inline DataScene::MetaDataInfo const &MetaData() const noexcept {
    return meta_data_;
  }

 protected:
  /// @brief show map info information
  void show_info() noexcept;
  /// @brief
  /// @param file
  /// @return
  DataScene::MetaDataInfo read_meta_data(
      std::filesystem::path const &file) const noexcept(false);

  ///@brief travel timestamp data file
  ///@param path root path
  ///@param files time stamp path
  void travel_timestamp_data_file(
      std::filesystem::path const &root,
      DataScene::DataFrameArrayPtr<std::filesystem::path> files_ptr) const
      noexcept(false);

  /// @brief
  /// @param sync_info_files_ptr
  /// @param sync_info_dict
  /// @param key_array_ptr
  void parse_sync_info_dict(
      DataScene::DataFrameArrayPtr<std::filesystem::path> const
          &sync_info_files_ptr,
      SyncInfoDict &sync_info_dict,
      DataScene::DataFrameArrayPtr<std::string> &key_array_ptr) const
      noexcept(false);

  /// @brief
  /// @param file_path
  /// @return
  SyncInfo parse_sync_info(std::filesystem::path const &file_path) const
      noexcept(false);

  /// @brief
  /// @param key
  /// @param catagory
  /// @return
  SyncInfoData parse_sync_info_data(std::string const &key,
                                    std::string const &catagory) const
      noexcept(false);

  /// @brief
  /// @param path
  /// @param data_calib_ptr
  void load_calib_data(
      std::filesystem::path const &path,
      DataScene::CalibDataArrayPtr<DataScene::MatrixData> calib_data_ptr) const
      noexcept(false);

  /// @brief
  /// @param path
  /// @param channels
  /// @param point_cloud
  void read_lidar_data(std::filesystem::path const &path,
                       std::vector<std::string> const &channels,
                       DataScene::LidarPointCloud &point_cloud) const
      noexcept(false);

  /// @brief
  /// @tparam T
  /// @param c
  /// @param v
  /// @return
  template <typename T>
  std::int32_t find_index(std::vector<T> const &c, T const &v) const noexcept {
    std::int32_t index{invalidIndex};
    auto it{std::find(c.begin(), c.end(), v)};
    if (c.end() != it) {
      index = std::distance(c.begin(), it);
    }
    return index;
  }

 private:
  /// @brief init flag
  bool is_init_;
  /// @brief  init call once flag
  std::once_flag init_flag_;
  /// @brief scene path
  std::filesystem::path path_;
  /// @brief meta data
  DataScene::MetaDataInfo meta_data_;
  /// @brief data frame keys
  DataScene::DataFrameArrayPtr<std::string> data_frame_keys_ptr_;
  /// @brief sync info file list
  DataScene::DataFrameArrayPtr<std::filesystem::path> sync_info_files_ptr_;
  /// @brief sync info dictionary
  SyncInfoDict sync_info_dict_;
  /// @brief calib data array ptr
  DataScene::CalibDataArrayPtr<DataScene::MatrixData> calib_data_ptr_;
};
}  // namespace perception
}  // namespace autodrive
#endif  // DATA_SCENE_IMPL_H_
